using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.UI;
using System.Xml.XPath;
using MimeCloud.Foundations.ErrorLogging;

namespace MimeCloud.Business.Helpers
{
    /// <summary>
    /// Contains a set of helper functions relevant to website operations
    /// </summary>
    /// <remarks>Based on code donated by Alex Norcliffe</remarks>
    public class Web
    {
        public const string UnsafePathChars = "&;:/#";
        private static readonly Regex UnsafePathRegex = new Regex(string.Format("[{0}]", Regex.Escape(UnsafePathChars)));
        private static readonly Regex SafePathRegex = new Regex(@"__([0-9a-fA-F]+)__");
        
        public static string AppendCssClass(string cssClass, string newClass)
        {
            string existingTrimmed = (cssClass ?? "").Trim();
            string newClassTrimmed = (newClass ?? "").Trim();
            string returnValue = existingTrimmed;
            if (!string.IsNullOrEmpty(newClass))
            {
                if (!string.IsNullOrEmpty(returnValue))
                {
                    returnValue += " ";
                }
                returnValue += newClassTrimmed;
            }
            return returnValue;
        }
        
        public static string CombineCssClasses(params string[] cssClasses)
        {
            string returnClass = "";
            foreach (string extraClass in cssClasses)
            {
                returnClass = AppendCssClass(returnClass, extraClass);
            }
            return returnClass;
        }
        
        public static string AmpersandSafeUrlEncode(string str)
        {
            return HttpUtility.UrlPathEncode(str == null ? null : UnsafePathRegex.Replace(str, GetHexValue));
        }
        
        private static string GetHexValue(Match m)
        {
            return string.Format("__{0}__",  Convert.ToInt32(m.Value[0]).ToString("X2"));
        }

        public static string AmpersandSafeUrlDecode(string str)
        {
            string returnString = HttpUtility.UrlDecode(str);
            return SafePathRegex.Replace(returnString, GetCharValue);
        }

        private static string GetCharValue(Match m)
        {
            try
            {
                return Char.ConvertFromUtf32(Convert.ToInt32(m.Groups[1].Value, 16));
            }
            catch (Exception)
            {
            }
            return "";
        }

        /// <summary>
        /// Gets the directory name of a path
        /// </summary>
        /// <param name="uriPath"></param>
        /// <returns></returns>
        public static string GetUriDirectoryName(string uriPath)
        {
            string trimmedPath = uriPath.Trim('/');
            int LastSlash = trimmedPath.LastIndexOf('/');
            if (LastSlash == 0)
            {
                // Root folder
                return trimmedPath;
            }
            else
                return trimmedPath.Substring(LastSlash + 1);
        }

        /// <summary>
        /// Returns a Uri based on a Url. If an app-relative Url is provided, creation of an absolute Url is attempted. An exception will be thrown if this is not possible. The Uri should be checked for validity
        /// </summary>
        /// <param name="Url"></param>
        /// <returns></returns>
        public Uri AttemptToMakeUri(string Url)
        {
            //make sure url starts with a protocol and a host
            if (!Url.StartsWith("http"))
            {
                if (Url.StartsWith("/"))
                {
                    string SiteHost = GetSiteHost();
                    Url = string.Format("{0}{1}", SiteHost, Url);
                }
                Url = string.Format("http://{0}", Url);
            }
            //attempt to create the Uri
            return new Uri(Url);
        }

        /// <summary>
        /// Attempt to return the current web application's hostname, either from the web.config or from the current request
        /// </summary>
        /// <returns></returns>
        public static string GetSiteHost()
        {
            string SiteHost = "";
            try
            {
                SiteHost = ConfigurationManager.AppSettings["Site.DefaultUrl"];
                Uri tmpUri = new Uri(SiteHost);
                SiteHost = tmpUri.Host;
            }
            catch (Exception ex)
            {
                ErrorHelper.LogError(new Exception("Error retreiving DefaultUrl Host from Settings", ex));
            }

            if (string.IsNullOrEmpty(SiteHost))
            {
                //attempt to get host from current request
                SiteHost = HttpContext.Current.Request.Url.Host;
            }
            return SiteHost;
        }

        /// <summary>
        /// Recursively searches a control tree for a matching control ID
        /// </summary>
        /// <param name="sourceControl">The control from which to start searching</param>
        /// <param name="id">The control ID to look for</param>
        /// <returns></returns>
        public static Control FindControl(Control sourceControl, string id)
        {
            if (string.Compare(sourceControl.ID, id, true) == 0)
                return (sourceControl);
            foreach (Control child in sourceControl.Controls)
            {
                Control recurse = FindControl(child, id);
                if (!(recurse == null))
                    return recurse;
            }
            return null;
        }

        /// <summary>
        /// Searches the QueryString and Form collections, returning the first value whose Key
        /// ends with the keySuffix parameter
        /// </summary>
        /// <param name="keySuffix">Suffix to search for</param>
        /// <returns>string value of the post parameter, or string.Empty if none is found</returns>
        public static string FindPostParameterBySuffix(string keySuffix)
        {
            foreach (string k in HttpContext.Current.Request.QueryString.Keys)
            {
                if (k.EndsWith(keySuffix, StringComparison.InvariantCultureIgnoreCase))
                    return HttpContext.Current.Request.QueryString[k];
            }
            foreach (string k in HttpContext.Current.Request.Form.Keys)
            {
                if (k.EndsWith(keySuffix, StringComparison.InvariantCultureIgnoreCase))
                    return HttpContext.Current.Request.Form[k];
            }
            return string.Empty;
        }

        /// <summary>
        /// Combines a Url with a given QueryString identifying whether a ? or an & is an appropriate separator.
        /// </summary>
        /// <param name="baseUrl">Base Url</param>
        /// <param name="inputQueryString">QueryString value to add to the base Url</param>
        /// <returns>string concatenation of the two input parameters</returns>
        public static string CombineUrlWithQueryString(string baseUrl, string inputQueryString)
        {
            string finalUrl;
            if (baseUrl.IndexOf('?') > 0)
                finalUrl = string.Format("{0}&{1}", baseUrl, inputQueryString);
            else
                finalUrl = string.Format("{0}?{1}", baseUrl, inputQueryString);

            return finalUrl.Replace("?&", "?");
        }

        /// <summary>
        /// Combines the given url with a new querystring parameter of <value>displayMsg</value>
        /// </summary>
        /// <param name="baseUrl"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public static string CombineMessageWithQueryString(string baseUrl, string message)
        {
            return CombineUrlWithQueryString(baseUrl, string.Concat("displayMsg=", HttpUtility.UrlEncode(message)));
        }

        /// <summary>
        /// Gets the value of the querystring parameter <value>displayMsg</value>, or returns the default value provided if it
        /// does not exist
        /// </summary>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static string GetMessageFromQueryString(string defaultValue)
        {
            return PostParams.GetString("displayMsg", HttpUtility.UrlDecode(defaultValue));
        }

        /// <summary>
        /// Converts URL QueryString into a NameValueCollection
        /// </summary>
        /// <param name="url">URL containing querystring</param>
        /// <returns>NameValueCollection of the URL</returns>
        public static NameValueCollection GetNameValueCollectionFromUrl(string url)
        {
            NameValueCollection QueryStringCollection = new NameValueCollection();
            //strip string data before the question mark
            if (url.IndexOf('?') >= 0)
                url = url.Remove(0, url.IndexOf('?') + 1);

            Array collection = url.Split("&".ToCharArray());

            for (int i = 0; i < collection.Length; i++)
            {
                string[] pairs = collection.GetValue(i).ToString().Split("=".ToCharArray());

                //check pair exists
                if (pairs.Length == 2)
                    QueryStringCollection.Add(pairs[0], pairs[1]);
            }
            return QueryStringCollection;
        }

        /// <summary>
        /// Provides a static method to map a virtual path to an absolute one by combining it with the value of <see cref="HttpRuntime.AppDomainAppPath"/>
        /// </summary>
        /// <param name="relativeFilePath"></param>
        /// <returns></returns>
        /// <remarks>Note that if the path is already rooted no modification is performed</remarks>
        public static string StaticMapPath(string relativeFilePath)
        {
            #region Validate Parameters

            if (relativeFilePath == null)
                // Set to an empty string rather than throwing an exception
                relativeFilePath = "";

            #endregion

            if (Path.IsPathRooted(relativeFilePath))
                return relativeFilePath;
            else
            {
                string rootPath = HttpRuntime.AppDomainAppPath;
                relativeFilePath = relativeFilePath.Replace("/", "\\").TrimStart('~').TrimStart('\\');
                return Path.Combine(rootPath, relativeFilePath);
            }
        }

        /// <summary>
        /// Establishes whether a path could have been generated from calling <see cref="GenerateSafePathForSerialization"/>
        /// on this machine and within this same <see cref="HttpRuntime.AppDomainAppId"/>, by checking to see if it starts with <see cref="GetSuffixForAppDomainSpecificFilename"/>.
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static bool IsSerializedPathFromThisAppDomain(string path)
        {
            #region Validate Parameters

            if (string.IsNullOrEmpty(path))
                throw new ArgumentNullException("path");

            #endregion

            path = Path.GetFileName(path) + "";
            if (path.StartsWith(GetSuffixForAppDomainSpecificFilename(), StringComparison.InvariantCultureIgnoreCase))
                return true;
            else
                return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static string GetSuffixForAppDomainSpecificFilename()
        {
            return
                string.Concat(
                    IO.GetSafeFileName(string.Format("{0}/{1}", Environment.MachineName, HttpRuntime.AppDomainAppId)),
                    "_");
        }

        public static string GenerateSafePathForSerialization(string rootDirPath, string fileNameSeed)
        {
            #region Validate Parameters

            if (string.IsNullOrEmpty(rootDirPath))
                throw new ArgumentNullException("rootDirPath");

            if (string.IsNullOrEmpty(fileNameSeed))
                throw new ArgumentNullException("fileNameSeed");

            #endregion

            return Path.Combine(rootDirPath, GenerateSafeFilenameForSerialization(fileNameSeed));
        }

        public static string GenerateSafeFilenameForSerialization(string fileNameSeed)
        {
            #region Validate Parameters

            if (string.IsNullOrEmpty(fileNameSeed))
                throw new ArgumentNullException("fileNameSeed");

            #endregion

            // Generate a filename for the serialized version of the cache item,
            // prefixing it with the machine name so that we can identify the source
            // of the serialized file without having to incur a deserialization hit
            return
                string.Concat(GetSuffixForAppDomainSpecificFilename(), IO.GetSafeFileName(fileNameSeed),
                              IO.SerializationFileNameExtension);
        }

        public static string GetStringFromNode(XPathNodeIterator xPathIterator, string xpath)
        {
            XPathNavigator node = xPathIterator.Current.SelectSingleNode(xpath);
            if (node != null)
                return node.Value;
            return string.Empty;
        }

        public static bool GetBooleanFromNode(XPathNodeIterator xPathIterator, string xpath)
        {
            XPathNavigator node = xPathIterator.Current.SelectSingleNode(xpath);
            if (node != null)
                return node.ValueAsBoolean;
            return false;
        }

        public static Uri GetUriFromNode(XPathNodeIterator xPathIterator, string xpath)
        {
            XPathNavigator node = xPathIterator.Current.SelectSingleNode(xpath);
            if (node != null && !string.IsNullOrEmpty(node.Value))
                return new Uri(node.Value);
            return null;
        }

        public static string RemoveExtension(string path)
        {
            int extensionIndex = path.LastIndexOf('.');
            return extensionIndex >= 0 ? path.Substring(0, extensionIndex) : path;
        }

        /// <summary>
        /// Combine web url paths.
        /// </summary>
        /// <param name="basePath"></param>
        /// <param name="relativePath"></param>
        /// <returns></returns>
        public static string CombinePaths(string basePath, string relativePath)
        {
            //return VirtualPathUtility.Combine(basePath + (basePath.EndsWith("/") ? "" : "/"), relativePath);
            string returnString = basePath;
            if (!string.IsNullOrEmpty(relativePath))
            {
                relativePath = relativePath.TrimStart('/');
                returnString += ((returnString.EndsWith("/") ? "" : "/") + relativePath);
            }
            return returnString;
        }

        public static string CombinePaths(string basePath, params string[] relativePaths)
        {
            string returnPath = basePath;
            foreach (string relativePath in relativePaths)
            {
                /*
                 * When an invocation is ambiguous,
                 * the compiler chooses the non-params
                 * method.
                 */
                returnPath = CombinePaths(returnPath, relativePath);
            }
            return returnPath;
        }

        /// <summary>
        /// Implements helper functions for dealing with HttpCookies, working around some .NET and browser bugs
        /// </summary>
        public class Cookies
        {
            /// <summary>
            /// Expires a cookie on the client, avoiding a few gotchas on how browsers differ in their support of Expiry date.
            /// </summary>
            /// <param name="name"></param>
            public static void ExpireCookie(string name)
            {
                // See notes in http://www.codeproject.com/aspnet/aspnetcookies.asp
                // for gotchas on how browsers differ in their support of Expiry date.

                // Methodology here: DO NOT simply remove the cookie from the Response.Cookies collection;
                // all this does is prevent a new cookie value from being sent to the browser in order to
                // update the client.
                // Equally, DO NOT use Expires = DateTime.Now in case the server time differs 
                // from the client time.
                // Instead, create a new blank cookie with the same name, set it to Expire 10 years ago
                // (see article on why you don't use DateTime.MinValue), remove any existing cookie
                // with that name from the collection, and replace it with this new expiring cookie 
                // in the Response.Cookies collection.

                HttpCookie replacementCookie = new HttpCookie(name);
                replacementCookie.Expires = DateTime.Now.AddYears(-10);
                replacementCookie.Value = "Something for browsers that don't support blank values";

                HttpContext.Current.Response.Cookies.Remove(name);
                HttpContext.Current.Response.Cookies.Add(replacementCookie);
            }
        }

        /// <summary>
        /// Contains static methods for working with emails and email addresses
        /// </summary>
        public static class Email
        {
            /// <summary>
            /// checks whether an address is syntacticly valid. Note that this verifies only syntactical validity and not whether the email address exists or not.
            /// </summary>
            /// <param name="inputEmail">string to check for email address syntax validation</param>
            /// <returns></returns>
            public static bool IsValidEmail(string inputEmail)
            {
                string strRegex = @"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*";
                Regex re = new Regex(strRegex);
                if (re.IsMatch(inputEmail))
                    return (true);
                else
                    return (false);
            }
        }

        public class PathInfo
        {
            /// <summary>
            /// Is key present in path info
            /// </summary>
            /// <param name="key">Name of key</param>
            /// <returns><see cref="bool"/> indicating whether the key was found</returns>
            public static bool IsPresent(string key)
            {
                bool found = false;

                //get path info keys
                string[] pathKeys =
                    HttpContext.Current.Request.PathInfo.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries);

                if (pathKeys.Length > 0)
                {
                    foreach (string s in pathKeys)
                    {
                        // check if key exists
                        if (s.IndexOf(key) == 0)
                        {
                            return true;
                        }
                    }
                }

                //check if it exists in query string
                if (!found)
                {
                    if (HttpContext.Current.Request.QueryString[key] != null)
                        found = true;
                }

                return found;
            }
            public static List<string> GetPresent(string prefix)
            {
                return GetPresent(prefix, HttpContext.Current.Request.PathInfo);
            }

            public static List<string> GetPresent(string prefix, string pathInfo)
            {
                //get path info keys
                return
                    pathInfo.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries).Where
                        (pi => pi.ToLower().StartsWith(prefix.ToLower())).ToList();
            }
            public static List<KeyValuePair<string, string>> GetDictionary(string keyPrefix)
            {
                return GetDictionary(keyPrefix, HttpContext.Current.Request.PathInfo);
            }

            public static List<KeyValuePair<string,string>> GetDictionary(string keyPrefix, string pathInfo)
            {
                bool found = false;
                char keyValuePairSeparator = '/';
                char keyValueSeparator = ',';

                //get path info keys
                string[] pathKeys = pathInfo.Split(new[] {keyValuePairSeparator},
                                                                               StringSplitOptions.RemoveEmptyEntries);

                List<KeyValuePair<string, string>> results = new List<KeyValuePair<string,string>>();

                if (pathKeys.Length > 0)
                {
                    foreach (string s in pathKeys)
                    {
                        //check if key exists
                        if (s.StartsWith(keyPrefix, StringComparison.OrdinalIgnoreCase))
                        {
                            string mdkey;
                            string mdvalue;

                            //get value from key
                            string[] value = s.Split(new[] {keyValueSeparator}, StringSplitOptions.RemoveEmptyEntries);
                            mdkey = value[0].Replace(keyPrefix,String.Empty);
                            if (value.Length == 2)
                                mdvalue = value[1];
                            else if (value.Length > 2)
                            {
                                StringBuilder sb = new StringBuilder();
                                for (int i = 1; i < value.Length; i++)
                                    sb.Append(string.Concat(value[i], keyValueSeparator));
                                // remove the last keyValueSeparator from the return value
                                sb.Remove(sb.Length - 1, 1);
                                mdvalue = sb.ToString();
                            }
                            else
                                mdvalue = string.Empty;

                            results.Add(new KeyValuePair<string, string>(mdkey, mdvalue));

                        }
                    }
                }
                return results;
            }

            /// <summary>
            /// Get value of item in path info
            /// </summary>
            /// <param name="key">Name of key</param>
            /// <returns>Value of item with specified key</returns>
            public static string GetValue(string key)
            {
                return GetValue(key, false);
            }

            /// <summary>
            /// Get value of item in input string
            /// </summary>
            /// <param name="key">Name of key</param>
            /// <param name="stringToSearch">The string to search for the key</param>
            /// <returns>Value of item with specified key</returns>
            public static string GetValue(string key, string stringToSearch)
            {
                return GetValue(key, false, stringToSearch);
            }

            /// <summary>
            /// Get value of item in path info
            /// </summary>
            /// <param name="key">Name of key</param>
            /// <returns>Value of item with specified key</returns>
            public static string GetValue(string key, bool keyValueSeparatedByPath)
            {
                string returnValue = GetValue(key, keyValueSeparatedByPath, HttpContext.Current.Request.PathInfo);
                if (string.IsNullOrEmpty(returnValue) && HttpContext.Current.Request.QueryString[key] != null)
                {
                    returnValue = HttpContext.Current.Request.QueryString[key];
                }
                return returnValue;
            }

            /// <summary>
            /// Get value of item in input string
            /// </summary>
            /// <param name="key">Name of key</param>
            /// <param name="stringToSearch">The string to search for the key</param>
            /// <returns>Value of item with specified key</returns>
            public static string GetValue(string key, bool keyValueSeparatedByPath, string stringToSearch)
            {
                bool found = false;
                char keyValuePairSeparator = '/';
                char keyValueSeparator = ',';

                //get path info keys
                string[] pathKeys = stringToSearch.Split(new[] {keyValuePairSeparator},
                                                                               StringSplitOptions.RemoveEmptyEntries);

                if (pathKeys.Length > 0)
                {
                    for (int i1 = 0; i1 < pathKeys.Length; i1++)
                    {
                        string s = pathKeys[i1];
                        //check if key exists

                        if (!keyValueSeparatedByPath && s.StartsWith(string.Concat(key, keyValueSeparator), StringComparison.OrdinalIgnoreCase))
                        {
                                //get value from key
                                string[] value = s.Split(new[] {keyValueSeparator},
                                                         StringSplitOptions.RemoveEmptyEntries);

                                if (value.Length == 2)
                                    return value[1];
                                else if (value.Length > 2)
                                {
                                    StringBuilder sb = new StringBuilder();
                                    for (int i = 1; i < value.Length; i++)
                                        sb.Append(string.Concat(value[i], keyValueSeparator));
                                    // remove the last keyValueSeparator from the return value
                                    sb.Remove(sb.Length - 1, 1);
                                    return sb.ToString();
                                }
                        }

                        //slash separator - no else if or check for keyValueSeparatedByPath parameter as can fallback to this
                        if (string.Compare(s.Trim(),key.Trim(),StringComparison.InvariantCultureIgnoreCase)==0)
                        {
                            if (pathKeys.Length > i1 + 1)
                            {
                                return pathKeys[i1 + 1];
                            }
                        }
                    }
                }
                //check if it exists in query string
                if (!found)
                {
                    if (HttpContext.Current.Request.QueryString[key] != null)
                        return HttpContext.Current.Request.QueryString[key];
                }

                return string.Empty;
            }
        
            /// <summary>
            /// Strip path info from a url path.
            /// Does not currently work with url scheme + host name.
            /// </summary>
            /// <param name="path"></param>
            /// <returns></returns>
            public static string StripPathInfo(string path)
            {
                return Regex.Match(path, @"/[\w/-]+\.[\w]+").ToString();
            }
        
        }

        /// <summary>
        /// Contains strongly-typed methods for retrieving values from the QueryString and Form collections
        /// </summary>
        public class PostParams
        {
            /// <summary>
            /// Returns a value from the HttpRequest.Params collection
            /// </summary>
            /// <param name="key">Key to lookup the value from the collection</param>
            /// <param name="defaultValue">Default value to be returned if the collection does not contain the Key</param>
            /// <returns>object if the Key is found, or null if it is not</returns>
            public static object GetValue(string key, object defaultValue)
            {
                return GetValue(key, defaultValue, HttpContext.Current.Request.Params);
            }

            /// <summary>
            /// Returns a value from a NameValueCollection
            /// </summary>
            /// <param name="key">Key to lookup the value from the collection</param>
            /// <param name="defaultValue">Default value to be returned if the collection does not contain the Key</param>
            /// <param name="collection"><see cref="NameValueCollection" /> containing post parameters</param>
            /// <returns>object if the Key is found, or null if it is not</returns>
            public static object GetValue(string key, object defaultValue, NameValueCollection collection)
            {
                object o = collection[key];
                if (o == null)
                    o = defaultValue;
                return o;
            }
            

            /// <summary>
            /// Returns a value from the HttpRequest.Params collection strongly typed to a string
            /// </summary>
            /// <param name="key">Key to lookup the value from the collection</param>
            /// <param name="defaultValue">Default value to be returned if the collection does not contain the Key</param>
            /// <returns>string value if the Key is found, or defaultValue if it is not</returns>
            public static string GetString(string key, string defaultValue)
            {
                object o = GetValue(key, defaultValue).ToString();
                if (o == null)
                    return defaultValue;
                else
                    return o.ToString();
            }

            public static List<String> GetStrings (string key)
            {
                return GetStrings(key, HttpContext.Current.Request.Params);
            }

            public static List<String> GetStrings (string key, NameValueCollection collection)
            {
                if (collection[key]==null)
                    return new List<string>();
                else
                {
                    return collection[key].Split(',').ToList();
                }
            }
            
            /// <summary>
            /// Returns a value from a NameValueCollection strongly typed to a string
            /// </summary>
            /// <param name="key">Key to lookup the value from the collection</param>
            /// <param name="defaultValue">Default value to be returned if the collection does not contain the Key</param>
            /// <param name="collection"><see cref="NameValueCollection" /> containing post parameters</param>
            /// <returns>string value if the Key is found, or defaultValue if it is not</returns>
            public static string GetString(string key, string defaultValue, NameValueCollection collection)
            {
                object o = GetValue(key, defaultValue, collection).ToString();
                if (o == null)
                    return defaultValue;
                else
                    return o.ToString();
            }

            /// <summary>
            /// Returns a value from the HttpRequest.Params collection strongly typed to an Int32
            /// </summary>
            /// <param name="key">Key to lookup the value from the collection</param>
            /// <param name="defaultValue">Default value to be returned if the collection does not contain the Key</param>
            /// <returns>Int32 value if the Key is found, or defaultValue if it is not</returns>
            public static Int32 GetInt32(string key, Int32 defaultValue)
            {
                Int32 result = defaultValue;
                Int32.TryParse(GetString(key, defaultValue.ToString()), out result);
                return result;
            }

            /// <summary>
            /// Returns a value from a NameValueCollection strongly typed to an Int32
            /// </summary>
            /// <param name="key">Key to lookup the value from the collection</param>
            /// <param name="defaultValue">Default value to be returned if the collection does not contain the Key</param>
            /// <param name="collection"><see cref="NameValueCollection" /> containing post parameters</param>
            /// <returns>Int32 value if the Key is found, or defaultValue if it is not</returns>
            public static Int32 GetInt32(string key, Int32 defaultValue, NameValueCollection collection)
            {
                Int32 result = defaultValue;
                Int32.TryParse(GetString(key, defaultValue.ToString(), collection), out result);
                return result;
            }

            /// <summary>
            /// Returns a value from the HttpRequest.Params collection strongly typed to an Int64
            /// </summary>
            /// <param name="key">Key to lookup the value from the collection</param>
            /// <param name="defaultValue">Default value to be returned if the collection does not contain the Key</param>
            /// <returns>Int64 value if the Key is found, or defaultValue if it is not</returns>
            public static Int64 GetInt64(string key, Int64 defaultValue)
            {
                Int64 result = defaultValue;
                Int64.TryParse(GetString(key, defaultValue.ToString()), out result);
                return result;
            }

            /// <summary>
            /// Returns a value from a NameValueCollection strongly typed to an Int64
            /// </summary>
            /// <param name="key">Key to lookup the value from the collection</param>
            /// <param name="defaultValue">Default value to be returned if the collection does not contain the Key</param>
            /// <param name="collection"><see cref="NameValueCollection" /> containing post parameters</param>
            /// <returns>Int64 value if the Key is found, or defaultValue if it is not</returns>
            public static Int64 GetInt64(string key, Int64 defaultValue, NameValueCollection collection)
            {
                Int64 result = defaultValue;
                Int64.TryParse(GetString(key, defaultValue.ToString(), collection), out result);
                return result;
            }

            /// <summary>
            /// Returns a value from the HttpRequest.Params collection strongly typed to an Int32
            /// </summary>
            /// <param name="key">Key to lookup the value from the collection</param>
            /// <param name="defaultValue">Default value to be returned if the collection does not contain the Key</param>
            /// <returns>Boolean value if the Key is found, or defaultValue if it is not</returns>
            public static bool GetBoolean(string key, bool defaultValue)
            {
                bool result = defaultValue;
                bool.TryParse(GetString(key, defaultValue.ToString()), out result);
                return result;
            }

            /// <summary>
            /// Returns a value from a NameValueCollection strongly typed to an Int32
            /// </summary>
            /// <param name="key">Key to lookup the value from the collection</param>
            /// <param name="defaultValue">Default value to be returned if the collection does not contain the Key</param>
            /// <param name="collection"><see cref="NameValueCollection" /> containing post parameters</param>
            /// <returns>Boolean value if the Key is found, or defaultValue if it is not</returns>
            public static bool GetBoolean(string key, bool defaultValue, NameValueCollection collection)
            {
                bool result = defaultValue;
                bool.TryParse(GetString(key, defaultValue.ToString(), collection), out result);
                return result;
            }

            /// <summary>
            /// Returns a value from the HttpRequest.Params collection strongly typed to a string after base64 decoding the original value.
            /// </summary>
            /// <param name="key">Key to lookup the value from the collection</param>
            /// <param name="defaultValue">Default unencoded value to be returned if the collection does not contain the Key</param>
            /// <returns>String value (base64 decoded) if the Key is found, or defaultValue if it is not </returns>
            public static string GetBase64DecodedParameter(string key, string defaultValue)
            {
                string result = defaultValue;
                result = GetString(key, defaultValue);
                if (result != defaultValue)
                {
                    string decResult = Text.base64Decode(result);
                    if (!string.IsNullOrEmpty(decResult))
                        result = decResult;
                    else
                        result = defaultValue;
                }
                return result;
            }

            /// <summary>
            /// Encodes a string using base64 encryption and then prepares it for use as an Http parameter by Url encoding it.
            /// </summary>
            /// <param name="toEncode">value to encode</param>
            /// <returns>the base64 and Url encodied version of the provided value</returns>
            public static string Base64EncodeParameter(string toEncode)
            {
                return HttpUtility.UrlEncode(Text.base64Encode(toEncode));
            }
        }

        /// <summary>
        /// Implements a convenient set of methods for timing operations
        /// </summary>
        public class Timer
        {
            private static Hashtable _timers;

            private static Hashtable Timers
            {
                get
                {
                    if (_timers == null)
                        _timers = new Hashtable();
                    return _timers;
                }
            }

            /// <summary>
            /// Records the start time for the given timerName
            /// </summary>
            /// <param name="timerName">The unique name of the timer</param>
            public static void Start(string timerName)
            {
                Timers[timerName] = DateTime.Now;
            }

            /// <summary>
            /// Calculates the difference between the current system time, and when the Timer with timerName was started
            /// </summary>
            /// <param name="timerName">The unique name of the timer</param>
            /// <returns>TimeSpan indicating the length of time the specified Timer has been running</returns>
            public static TimeSpan Stop(string timerName)
            {
                DateTime d = (DateTime) Timers[timerName];
                if (!(d == DateTime.MinValue))
                    return DateTime.Now.Subtract(d);
                else
                    return TimeSpan.MinValue;
            }

            /// <summary>
            /// Generates a formatted string showing the value of <see cref="Stop"/> in seconds
            /// </summary>
            /// <param name="timerName">The unique name of the timer</param>
            /// <returns>string showing the number of seconds e.g. "0.42s"</returns>
            public static string StopFormatted(string timerName)
            {
                return Stop(timerName).TotalMilliseconds/1000 + "s";
            }
        }

        /// <summary>
        /// Gets the querystring specified. If no value is found then return the fallback value.
        /// </summary>
        /// <param name="queryKey">The key to search for in the querystring.</param>
        /// <param name="fallback">The fallback value.</param>
        /// <returns>A valid query value for the given key.</returns>
        public static string GetQuerystring(string queryKey, string fallback)
        {
            var query = HttpContext.Current.Request.QueryString.Get(queryKey);
            if (string.IsNullOrEmpty(query))
            {
                query = fallback.ToString();
            }

            return query;
        } 

        public static class WebPath
        {
            public static char[] PathSeparatorCharacters
            {
                get
                {
                    return new char[] { '/', Path.PathSeparator };
                }
            }

            public static char PathSeparator
            {
                get
                {
                    return '/';
                }
            }

            public static String TrimPathChars(string source)
            {
                return source.Trim(PathSeparatorCharacters);
            }

            public static string WebPathCombine(string string1, string string2)
            {
                return String.Concat(TrimPathChars(string1), PathSeparator, TrimPathChars(string2));
            }

            public static string WebPathCombine(params string[] paths)
            {
                string result = String.Empty;
                foreach (string s in paths)
                {
                    result = String.Concat(result, TrimPathChars(s), PathSeparator);
                }
                return result.TrimEnd(PathSeparatorCharacters);
            }
        }
    }
}